<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>父子组件通信-数据双向绑定-watch方式</title>
</head>
<body>
<!--
双向绑定逻辑：
1、初步定义模板（指定模板id）
2、构造组件对象（组件对象template属性指定模板id，props添加属性）
3、将子组件注册进父组件，也就是父组件的components属性指定组件名和组件对象
3、构造组件对象的data属性绑定props添加的属性，这样从父组件传到props属性上的值，就会直接和data属性的值进行绑定（也就是数据父传子），
组件内部直接使用data属性中对象的属性的值即可
4、构造组件内部v-model绑定data数据，实现数据双向绑定，比如：组件内部有个输入框，这个输入框的值绑定到了组件data的属性上，
也就是上一步所说的使用data属性即是父传子的数据，输入框数据变化会引起data中数据变化，我们用watch监听绑定的data中变量的变化，
在监听变量变化后执行的方法中自定义一个事件，并且将输入的值传进去，拿给父组件去监听这个自定义事件，父组件监听到自定义事件的变化，
拿到这个输入的值去更新自己data中的响应的属性即可，这样子组件数据的变化就更新了父组件响应的数据。（也就是数据子传父）
5、父组件监听子组件自定义的事件，拿到子组件传过来的值更新自己data属性中对应的对象的属性值
这样就实现了父子组件的数据的双向绑定，利用数据的父传子-props，数据的子传父-自定义事件即可实现父子组件的数据的双向绑定。
-->

<div id="app">

  <!--  使用组件-->
  <my-div :number1="num1" :number2="num2" @number1-change="num1Change" @number2-change="num2Change"/>

</div>

<!--模板信息定义-->
<template id="myDiv">
  <div>

    <p style="color: red">
      <span style="color: blue">效果说明：</span>number1是number2的100倍，当输入number1的值时候，number2的值也跟着变化，并且number2是number1的100倍；
      <br>当输入number2的时候，number1的值也跟着变化，number1是number2的1/100<br>
      <span style="color: blue">参数说明：</span>含有props字体的是对应的父组件相应参数，含有data的是子组件对应的参数值<br>
    </p>

    <p>props number1: {{number1}}</p>
    <p>data dNumber1: {{dNumber1}}</p>
    <!--    v-model绑定data中的dNumber1变量-->
    输入dNumber1的值：<input v-model="dNumber1">

    <!--    不要用v-model直接绑定props中的number1，虽然效果是子组件number1的值改变了，父组件的num1也会改变，
            看似也是实现了双向绑定，但是实际控制台已经报错了-->
    <!--    <input v-model="number1">-->

    <p>props number2: {{number2}}</p>
    <p>data dNumber2: {{dNumber2}}</p>
    <!--    v-model绑定data中的dNumber2变量-->
    输入dNumber2的值：<input v-model="dNumber2">
  </div>
</template>
<script src="../js/vue.js"></script>
<script>

  //【构造子组件对象（vue实例的子组件）】
  const myDiv = {
    template: "#myDiv", //绑定需要注册的组件
    props: { //添加属性，给组件添加标签属性，用对象来定义多个组件标签属性，对象的属性名称也就是组件标签属性
      number1: Number,
      number2: Number
    },
    data() { // 组件要绑定的数据
      return {
        dNumber1: this.number1,
        dNumber2: this.number2,
        testObj: {
          name: "测试人员",
          time: "2020-05-06"
        }
      }
    },
    // watch的作用可以监控一个值的变换，并调用因为变化需要执行的方法。可以通过watch动态改变关联的状态。
    watch: {
      dNumber1(newValue, oldValue) {  ////普通的watch监听(监听的是变量的值,比如这里dNumber1就是data中的变量)
        //自定义事件，并且传入默认值，这里是定义了一个number2-change事件，并且默认传入dNumber2值，向上传递，由父组件监听使用
        this.dNumber2 = newValue * 100
        this.$emit("number2-change", this.dNumber2)
      },
      dNumber2(newValue) {
        this.dNumber1 = newValue / 100;
        //自定义事件，并且传入默认值，这里是定义了一个number1-change事件，并且默认传入dNumber1值，向上传递，由父组件监听使用
        this.$emit("number1-change", this.dNumber1)
      },
      //当需要监听一个对象的改变时，普通的watch方法无法监听到对象内部属性的改变，只有data中的数据才能够监听到变化，此时就需要deep属性对对象进行深度监听。
      testObj: {//深度监听，可监听到对象、数组的变化
        handler(newValue, oldVal) {
          console.log("now testObj: " + newValue.name + " , old testObj" + oldVal.name)
        },
        deep: true, //true 深度监听
        // immediate表示在watch中首次绑定的时候，是否执行handler，值为true则表示在watch中声明的时候，就立即执行handler方法，值为false，则和一般使用watch一样，在数据发生变化的时候才执行handler。
        immediate: true //数据首次绑定时是否执行hander
      }
      //设置deep: true 则可以监听到testObj.name的变化，此时会给testObj的所有属性都加上这个监听器，当对象属性较多时，每个属性值的变化都会执行handler。如果只需要监听对象中的一个属性值，则可以做以下优化：使用字符串的形式监听对象属性
      /*
      'testObj.name': {
        handler(newValue, oldVal) {
          console.log("now testObj: " + newValue.name + " , old testObj" + oldVal.name)
        },
        deep: true, //true 深度监听
        // immediate表示在watch中首次绑定的时候，是否执行handler，值为true则表示在watch中声明的时候，就立即执行handler方法，值为false，则和一般使用watch一样，在数据发生变化的时候才执行handler。
        immediate: true //数据首次绑定时是否执行hander
      }
      */
    }
  }

  //根组件vue实例（也是myDiv组件的父组件）
  const app = new Vue({
    el: "#app", // 用于挂载要管理的元素，类似于jquery中$("#"),找到要操作的DOM节点
    data: { // 定义的数据，这些定义的数据，只在el 挂在的元素标签的内部才能使用解析
      num1: 1,
      num2: 0
    },
    // 【局部组件注册】
    // 注册组件，components属性，传入对象，对象的属性为组件的名称，值为构造的组件对象
    components: {
      myDiv //注册组件，这是es6增强写法，同名可省略，相当于myDiv : myDiv
    },
    methods: { // 定义的方法，这些定义的方法也是只有在el 挂在的元素标签的内部才能使用解析
      num1Change(number) {
        this.num1 = number;
      },
      num2Change(number) {
        this.num2 = number;
      },
    }
  });
</script>
</body>
</html>